#pragma once
#include <GLFW/glfw3.h>
#include <vulkan/vulkan_core.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include "ce_vulkan_struct.h"
#include "ce_vulkan_object.h"
#include "ce_vulkan_shader.h"
#include "ce_vulkan_pipeline.h"
#include "ce_vulkan_pass.h"




class CE_VulkanContext
{
private:
    /**
     * @brief 窗口句柄
     */
    GLFWwindow *_window;
    /**
     * @brief 物理设备
     */
    VkPhysicalDevice _physicalDevice;
    /**
     * @brief vulkan device
     */
    VkDevice _vkdevice;
    VkSurfaceKHR _surface; // 用于显示的窗口句柄

    /**
     * @brief 交换链
     * 本质上是一个等待被呈现在屏幕上的图像队列
     * 程序会在图像上进行绘制，然后再将其返回到队列中
     * Swap Chain的一般目的是使图像的呈现与屏幕的刷新率同步
     */
    VkSwapchainKHR _swapChain;
    std::vector<VkImage> _swapChainImages;
    VkFormat _swapChainImageFormat;
    VkExtent2D _swapChainExtent;

    void _createSwapChain();
    VkSurfaceFormatKHR _chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats);
    VkPresentModeKHR _chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes);
    VkExtent2D _chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities);
    /**
     * @brief 交换链图像视图
     * 为了在PipeLine中使用任何VkImage，包括Swap Chain中的那些Image，
     * 我们必须创建一个VkImageView对象。ImageView实际上是对图像的一种观察。
     * 它描述了如何访问图像以及访问图像的哪一部分，我们必须通过ImageView来能够读取Image
     */
    std::vector<VkImageView> _swapChainImageViews;
    void _createImageViews();
    /**
     * @brief 命令池
     * Command Pool有两种的Flag:
     * VK_COMMAND_POOL_CREATE_TRANSIENT_BIT: 提示Command Buffer非常频繁的重新记录新命令(可能会改变内存分配行为)
     * VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT: 允许Command Buffer被单独重新记录，没有这个标志，所有的Command Buffer都必须一起重置
     */
    /**
     * @brief ui pass
     * 专门负责ui渲染
     */
    // CE_VulkanPass *_uiPass;
    void _createPass();

    VkCommandPool _commandPool;
    void _createCommandPool();


    std::vector<VkSemaphore> _imageAvailableSemaphores; // 用于绘制的同步变量
    std::vector<VkSemaphore> _renderFinishedSemaphores;
    std::vector<VkFence> _inFlightFences;
    std::vector<VkFence> _imagesInFlight;

    void _createSyncObjects();

    /**
     * @brief 第一个清除对象
     */
    CE_VulkanObject *_firstClearObject;
    /**
     * @brief 创建第一个清除对象
     */
    void _createFirstClearObject();

    // std::vector<CE_VulkanObject *> _objects;
    size_t _currentFrame = 0;
    /**
     * 获取下一帧交换链图像
     * @return 图像索引
     * @throws std::runtime_error 如果获取图像失败
     */
    uint32_t _acquireNextImage();
    /**
     * 提交命令缓冲区到图形队列
     * @param imageIndex 当前帧图像索引
     * @param commandBuffers 需要提交的命令缓冲区集合
     */
    void _submitCommands(uint32_t imageIndex, const std::vector<VkCommandBuffer> &commandBuffers);
    /**
     * 呈现当前帧
     * @param imageIndex 当前帧图像索引
     */
    void _presentFrame(uint32_t imageIndex);

    /**
     * @brief ui对象
     */
    std::vector<std::string> _insertionOrder; // 单独记录插入顺序
    std::map<std::string, CE_VulkanObject *> _uiObjects;
    std::map<std::string, CE_VulkanShader *> _shaders;
    std::map<std::string, CE_VulkanPass *> _pass;
    std::map<std::string, CE_VulkanPipeline *> _pipelines;


public:
    CE_VulkanContext(GLFWwindow *window, VkPhysicalDevice physicalDevice, VkDevice vkdevice, VkSurfaceKHR surface);
    VkDevice getVKDevice();
    VkExtent2D getSwapChainExtent();
    
    std::vector<VkImageView> getSwapChainImageViews(); // 添加一个集合存储帧缓冲对象
    std::vector<VkImage> getSwapChainImages();
    
    VkCommandPool getCommandPool();

    void addUIObject(std::string id);
    void updateUIObjectShader(std::string id, std::string shaderPath);
    void updateUIObjectVertexs(std::string id,std::vector<float> positions, std::vector<int> indices);
    void updateUIObjectColor(std::string id, float r, float g, float b, float a);

    void drawFrame();

    ~CE_VulkanContext();
};
